/*
 * prabsyn.c - Print Abstract Syntax data structures. Most functions 
 *           handle an instance of an abstract syntax rule.
 */

#include <stdio.h>
#include "util.h"
#include "symbol.h" /* symbol table data structures */
#include "absyn.h"  /* abstract syntax data structures */
#include "prabsyn.h" /* function prototype */

/* local function prototypes */
static void pr_var(FILE *out, A_var v, int d);
static void pr_dec(FILE *out, A_dec v, int d);
static void pr_ty(FILE *out, A_ty v, int d);
static void pr_field(FILE *out, A_field v, int d);
static void pr_fieldList(FILE *out, A_fieldList v, int d);
static void pr_expList(FILE *out, A_expList v, int d);
static void pr_decList(FILE *out, A_decList v, int d);
static void pr_efield(FILE *out, A_efield v, int d);
static void pr_efieldList(FILE *out, A_efieldList v, int d);

static void indent(FILE *out, int d) {
  int i;
  for (i = 0; i <= d; i++) fprintf(out, " ");
}

/* Print A_var types. Indent d spaces. */
static void pr_var(FILE *out, A_var v, int d) {
  indent(out, d);
  switch (v->kind) {
    case A_simpleVar:
      fprintf(out, "simpleVar(%s)", S_name(v->u.simple)); 
      break;
    case A_fieldVar:
      fprintf(out, "%s\n", "fieldVar(");
      pr_var(out, v->u.field.var, d+1); fprintf(out, "%s\n", ","); 
      indent(out, d+1); fprintf(out, "%s)", S_name(v->u.field.sym));
      break;
    case A_subscriptVar:
      fprintf(out, "%s\n", "subscriptVar(");
      pr_var(out, v->u.subscript.var, d+1); fprintf(out, "%s\n", ","); 
      pr_exp(out, v->u.subscript.exp, d+1); fprintf(out, "%s", ")");
      break;
    default:
      assert(0); 
  } 
}

static char str_oper[][12] = {
  "PLUS", "MINUS", "TIMES", "DIVIDE", 
  "EQUAL", "NOTEQUAL", "LESSTHAN", "LESSEQ", "GREAT", "GREATEQ"};

static void pr_oper(FILE *out, A_oper d) {
  fprintf(out, "%s", str_oper[d]);
}

/* Print A_var types. Indent d spaces. */
void pr_exp(FILE *out, A_exp v, int d) {
  indent(out, d);
  switch (v->kind) {
    case A_varExp:
      fprintf(out, "varExp(\n"); pr_var(out, v->u.var, d+1); 
      fprintf(out, "%s", ")");
      break;
    case A_nilExp:
      fprintf(out, "nilExp()");
      break;
    case A_intExp:
      fprintf(out, "intExp(%d)", v->u.intt);
      break;
    case A_stringExp:
      fprintf(out, "stringExp(%s)", v->u.stringg);
      break;
    case A_callExp:
      fprintf(out, "callExp(%s,\n", S_name(v->u.call.func));
      pr_expList(out, v->u.call.args, d+1); fprintf(out, ")\n");
      break;
    case A_opExp:
      fprintf(out, "opExp(\n");
      indent(out, d+1); pr_oper(out, v->u.op.oper); fprintf(out, ",\n"); 
      pr_exp(out, v->u.op.left, d+1); fprintf(out, ",\n"); 
      pr_exp(out, v->u.op.right, d+1); fprintf(out, ")\n");
      break;
    case A_recordExp:
      fprintf(out, "recordExp(%s,\n", S_name(v->u.record.typ)); 
      pr_efieldList(out, v->u.record.fields, d+1); fprintf(out, ")\n");
      break;
    case A_seqExp:
      fprintf(out, "seqExp(\n");
      pr_expList(out, v->u.seq, d+1); fprintf(out, ")\n");
      break;
    case A_assignExp:
      fprintf(out, "assignExp(\n");
      pr_var(out, v->u.assign.var, d+1); fprintf(out, ",\n");
      pr_exp(out, v->u.assign.exp, d+1); fprintf(out, ")\n");
      break;
    case A_ifExp:
      fprintf(out, "iffExp(\n");
      pr_exp(out, v->u.iff.test, d+1); fprintf(out, ",\n");
      pr_exp(out, v->u.iff.then, d+1);
      if (v->u.iff.elsee) { /* else is optional */
        fprintf(out, ",\n");
        pr_exp(out, v->u.iff.elsee, d+1);
      }
      fprintf(out, ")\n");
      break;
    case A_whileExp:
      fprintf(out, "whileExp(\n");
      pr_exp(out, v->u.whilee.test, d+1); fprintf(out, ",\n");
      pr_exp(out, v->u.whilee.body, d+1); fprintf(out, ")\n");
      break;
    case A_forExp:
      fprintf(out, "forExp(%s,\n", S_name(v->u.forr.var)); 
      pr_exp(out, v->u.forr.lo, d+1); fprintf(out, ",\n");
      pr_exp(out, v->u.forr.hi, d+1); fprintf(out, "%s\n", ",");
      pr_exp(out, v->u.forr.body, d+1); fprintf(out, ",\n");
      indent(out, d+1); fprintf(out, "%s", v->u.forr.escape ? "TRUE)\n" : "FALSE)\n");
      break;
    case A_breakExp:
      fprintf(out, "breakExp()\n");
      break;
    case A_letExp:
      fprintf(out, "letExp(\n");
      pr_decList(out, v->u.let.decs, d+1); fprintf(out, ",\n");
      pr_expList(out, v->u.let.body, d+1); fprintf(out, ")\n");
      break;
    case A_arrayExp:
      fprintf(out, "arrayExp(%s,\n", S_name(v->u.array.typ));
      pr_exp(out, v->u.array.size, d+1); fprintf(out, ",\n");
      pr_exp(out, v->u.array.init, d+1); fprintf(out, ")");
      break;
    default:
      assert(0); 
  } 
}

static void pr_dec(FILE *out, A_dec v, int d) {
  indent(out, d);
  switch (v->kind) {
    case A_functionDec:
      fprintf(out, "fundec(%s,\n", S_name(v->u.function.name));
      pr_fieldList(out, v->u.function.params, d+1);
      fprintf(out, ",\n");
      if (v->u.function.result) {
        indent(out, d+1);
        fprintf(out, "%s,\n", S_name(v->u.function.result));
      }
      pr_exp(out, v->u.function.body, d+1);
      fprintf(out, ")");
      break;

    case A_varDec:
      fprintf(out, "varDec(%s,\n", S_name(v->u.var.var));
      if (v->u.var.typ) {
        indent(out, d+1);
        fprintf(out, "%s,\n", S_name(v->u.var.typ)); 
      }
      pr_exp(out, v->u.var.init, d+1);
      fprintf(out, ",\n");
      indent(out, d+1);
      fprintf(out, "%s", v->u.var.escape ? "TRUE)" : "FALSE)");
      break;

    case A_typeDec:
      fprintf(out, "typeDec(%s,\n", S_name(v->u.type.name)); 
      pr_ty(out, v->u.type.ty, d+1); fprintf(out, ")");
      break;
    default:
      assert(0); 
  } 
}

static void pr_ty(FILE *out, A_ty v, int d) {
  indent(out, d);
  switch (v->kind) {
    case A_nameTy:
      fprintf(out, "nameTy(%s)", S_name(v->u.name));
      break;
    case A_recordTy:
      fprintf(out, "recordTy(\n");
      pr_fieldList(out, v->u.record, d+1); fprintf(out, ")");
      break;
    case A_arrayTy:
      fprintf(out, "arrayTy(%s)", S_name(v->u.array));
      break;
    default:
      assert(0); 
  } 
}

static void pr_field(FILE *out, A_field v, int d) {
  indent(out, d);
  fprintf(out, "field(%s,\n", S_name(v->name));
  indent(out, d+1); fprintf(out, "%s,\n", S_name(v->typ));
  indent(out, d+1); fprintf(out, "%s", v->escape ? "TRUE)" : "FALSE)");
}

static void pr_fieldList(FILE *out, A_fieldList v, int d) {
  indent(out, d);
  if (v) {
    fprintf(out, "fieldList(\n");
    pr_field(out, v->head, d+1); fprintf(out, ",\n");
    pr_fieldList(out, v->tail, d+1); fprintf(out, ")");
  }
  else fprintf(out, "fieldList()");
}

static void pr_expList(FILE *out, A_expList v, int d) {
  indent(out, d);
  if (v) {
    fprintf(out, "expList(\n"); 
    pr_exp(out, v->head, d+1); fprintf(out, ",\n");
    pr_expList(out, v->tail, d+1);
    fprintf(out, ")\n");
  }
  else fprintf(out, "expList()\n"); 
}

static void pr_decList(FILE *out, A_decList v, int d) {
  indent(out, d);
  if (v) {
    fprintf(out, "decList(\n"); 
    pr_dec(out, v->head, d+1); fprintf(out, ",\n");
    pr_decList(out, v->tail, d+1);
    fprintf(out, ")");
  }
  else fprintf(out, "decList()"); 

}

static void pr_efield(FILE *out, A_efield v, int d) {
  indent(out, d);
  if (v) {
    fprintf(out, "efield(%s,\n", S_name(v->name));
    pr_exp(out, v->exp, d+1); fprintf(out, ")");
  }
  else fprintf(out, "efield()");
}

static void pr_efieldList(FILE *out, A_efieldList v, int d) {
  indent(out, d);
  if (v) {
    fprintf(out, "efieldList(\n"); 
    pr_efield(out, v->head, d+1); fprintf(out, ",\n");
    pr_efieldList(out, v->tail, d+1); fprintf(out, ")");
  }
  else fprintf(out, "efieldList()");
}
